programming4us
           
 
 
SQL Server

SQL server 2012 : T-SQL Enhancements - Table-Valued Parameters (part 1)

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
7/10/2013 7:22:49 PM

As the name implies, a table-valued parameter (TVP) lets you pass an entire set of rows as a single parameter to T-SQL stored procedures and user-defined functions (UDFs). This is extremely useful in and of itself, but arguably the most compelling facet of TVPs is their ability to marshal an entire set of rows across the network, from your .NET client to your SQL Server database, with a single stored procedure call (one roundtrip) and a single table-valued parameter.

Prior to SQL Server 2008, developers were forced to resort to clever hacks in an effort to reduce multiple roundtrips into one when inserting multiple rows—using techniques such as XML, delimited or encoded text, or even (gasp) accepting hundreds (up to 2100!) of parameters. But special logic then needs to be implemented for packaging and unpackaging the parameter values on both sides of the wire. Worse, the code to implement that logic is often gnarly and unmaintainable. None of those techniques even come close to the elegance and simplicity of using TVPs, which offer a native solution to this problem.

More Than Just Another Temporary Table Solution

A TVP is based on a user-defined table type, which you create to describe the schema for a set of rows that can be passed to stored procedures and UDFs. It’s helpful to begin understanding TVPs by first comparing them to similar “set” constructs, such as table variables, temp tables, and Common Table Expressions (CTEs). All of these provide a source of tabular data that you can query and join against, so you can treat a TVP, table variable, temporary table, or CTE just like you would an ordinary table or view in virtually any scenario.

CTEs and table variables store their row data in memory—assuming reasonably sized sets that don’t overflow the RAM cache allocated for them, in which case, they do push their data into tempdb. In contrast, a TVP’s data is always stored in tempdb. When you first populate a TVP, SQL Server creates a table in tempdb to back that TVP as it gets passed from one stored procedure (or UDF) to another. Once the stack unwinds and the TVP falls out of scope in your T-SQL code, SQL Server cleans up tempdb automatically. You never interact directly with tempdb, because TVPs provide a total abstraction over it.

The true power of TVPs lies in the ability to pass an entire table (a set of rows) as a single parameter from client to server, and between your T-SQL stored procedures and user-defined functions. Table variables and temporary tables, on the other hand, cannot be passed as parameters. CTEs are limited in scope to the statement following their creation and are therefore inherently incapable of being passed as parameters.

Reusability is another side benefit of TVPs. The schema of a TVP is centrally maintained, which is not the case with table variables, temporary tables, and CTEs. You define the schema once by creating a new user-defined type (UDT) of type table, which you do by applying the AS TABLE clause to the CREATE TYPE statement, as shown in Example 1.

Example 1. Defining the schema for a user-defined table type.

CREATE TYPE CustomerUdt AS TABLE
  (Id int,
   CustomerName nvarchar(50),
   PostalCode nvarchar(50))

This statement creates a new user-defined table type named CustomerUdt with three columns. TVP variables of type CustomerUdt can then be declared and populated with rows of data that fit this schema, and SQL Server will store and manage those rows in tempdb behind the scenes. These variables can be passed freely between stored procedures—unlike regular table variables, which are stored in RAM behind the scenes and cannot be passed as parameters. When TVP variables declared as CustomerUdt fall out of scope and are no longer referenced, the underlying data in tempdb supporting the TVP is deleted automatically by SQL Server.

You can see that, in fact, a TVP is essentially a user-defined table type. Populated instances of this type can be passed on as parameters to stored procedures and user-defined functions—something you still can’t do with a regular table variable. Once the table type is defined, you can create stored procedures with parameters of that type to pass an entire set of rows using TVPs.

TVP types are displayed in Visual Studio’s SQL Server Object Explorer in the User-Defined Table Types node beneath Programmability | Types, as shown in Figure 1 (as it does in SQL Server Management Studio’s Object Explorer).

There are many practical applications for passing entire sets of data around as parameters, and we’ll explore a number of them in the rest of this section.

Submitting Orders

A typical scenario in which TVPs can be applied is an order entry system. When a customer places an order, a new order row and any number of new order detail rows must be created in the database. Traditionally, this might be accomplished by creating two stored procedures—one for inserting an order row and one for inserting an order detail row. The application would invoke a stored procedure call for each individual row, so for an order with 20 details, there would be a total of 21 stored procedure calls (1 for the order and 20 for the details). There could of course be even larger orders with many more than 20 details. As a result, numerous roundtrips are made between the application and the database, each one carrying only a single row of data.

User-defined table types that can be used for TVPs displayed in SQL Server Object Explorer.

Figure 1. User-defined table types that can be used for TVPs displayed in SQL Server Object Explorer.

Enter TVPs. Now you can create a single stored procedure with just two TVPs, one for the order row and one for the order details rows. The client can now issue a single call to this stored procedure, passing to it the entire order with all its details, as shown in Example 2.

Note

The code in Example 2 assumes that the Order and OrderDetail tables already exist, and that the OrderUdt and OrderDetailUdt table types have already been created with a column schema that matches the tables.

Example 2. Creating a stored procedure that accepts TVPs.

CREATE PROCEDURE uspInsertNewOrder
 (@OrderTvp AS OrderUdt READONLY,
  @OrderDetailsTvp AS OrderDetailUdt READONLY)
AS
     INSERT INTO [Order]
      SELECT * FROM @OrderTvp

     INSERT INTO [OrderDetail]
      SELECT * FROM @OrderDetailsTvp

As you can see, this code inserts into the Order and OrderDetail tables directly from the rows passed in through the two TVPs. You are essentially performing a bulk insert with a single call, rather than individual inserts across multiple calls wrapped in a transaction.

We’ll now take look at the bulk insert possibilities for TVPs and how to create, declare, populate, and pass TVPs in T-SQL. Then we’ll demonstrate how to populate TVPs and pass them across the network from .NET client application code to stored procedures using ADO.NET.

Using TVPs for Bulk Inserts and Updates

Here’s an example of a stored procedure that you can create in the AdventureWorks2012 database that accepts a TVP and inserts all of the rows that get passed in through it into the Product.Location table. By creating a user-defined table type named LocationUdt that describes the schema for each row passed to the stored procedure, any code can call the stored procedure and pass to it a set of rows for insertion into Product.Location using a single parameter typed as LocationUdt.

First, create the user-defined table data type LocationUdt, as shown in Example 3.

Example 3. Creating the LocationUdt table type to be used for bulk operations with TVPs.

CREATE TYPE LocationUdt AS TABLE(
  LocationName varchar(50),
  CostRate int)

Now a TVP variable of this type can be declared to hold a set of rows with the two columns LocationName and CostRate. These rows can be fed to a stored procedure by passing the TVP variable into it. The stored procedure can then select from the TVP just like a regular table or view and thus use it as the source for an INSERT INTO…SELECT statement that appends each row to the Product.Location table.

Rows added to Product.Location require more than just the two fields for the location name and cost rate. The table also needs values for the availability and modified date fields, which you’ll let the stored procedure handle. What you’re doing here is defining a schema that can provide a subset of the required Product.Location fields (Name and CostRate), for passing multiple rows of data to a stored procedure that provides values for the remaining required fields (Availability and ModifiedDate). In the example, the stored procedure sets Availability to 0 and ModifiedDate to the GETDATE function on each row of data inserted from the TVP (passed in as the only parameter) that provides the values for Name and CostRate, as shown in Example 4.

Example 4. Creating a stored procedure to perform a bulk insert using a TVP declared as the LocationUdt table type.

CREATE PROCEDURE uspInsertProductionLocation
 (@TVP LocationUdt READONLY)
AS
     INSERT INTO [Production].[Location]
       ([Name], [CostRate], [Availability], [ModifiedDate])
      SELECT *, 0, GETDATE() FROM @TVP

You now have a stored procedure that can accept a TVP containing a set of rows with location names and cost rates to be inserted into the Production.Location table, and that sets the availability quantity and modified date on each inserted row—all achieved with a single parameter and a single INSERT INTO…SELECT statement! The procedure doesn’t know or care how the caller populates the TVP before it is used as the source for the INSERT INTO…SELECT statement. For example, the caller could manually add one row at a time, as follows:

DECLARE @LocationTvp AS LocationUdt

INSERT INTO @LocationTvp VALUES('UK', 122.4)
INSERT INTO @LocationTvp VALUES('Paris', 359.73)

EXEC uspInsertProductionLocation @LocationTvp

Or the caller could bulk insert into the TVP from another source table using INSERT INTO…SELECT, as in the next example. You will fill the TVP from the existing Person.StateProvince table using the table’s Name column for LocationName and the value 0 for CostRate. Passing this TVP to the stored procedure will result in a new set of rows added to Production.Location with their Name fields set according to the names in the Person.StateProvince table, their CostRate and Availability values set to 0, and their ModifiedDate values set by GETDATE, as shown here:

DECLARE @LocationTVP AS LocationUdt

INSERT INTO @LocationTVP
 SELECT [Name], 0.00 FROM [Person].[StateProvince]

EXEC uspInsertProductionLocation @LocationTVP

The TVP could also be populated on the client using ADO.NET, as you’ll learn in the next section.

Bulk updates (and deletes) using TVPs are possible as well. You can create an UPDATE statement by joining a TVP (which you must alias) to the table you want to update. The rows updated in the table are determined by the matches joined to by the TVP and can be set to new values that are also contained in the TVP. For example, you can pass a TVP populated with category IDs and names for updating a Category table in the database, as shown in Example 5. By joining the TVP to the Category table on the category ID, the uspUpdateCategories stored procedure can update all the matching rows in the Category with the new category names passed in the TVP.

Example 5. Bulk updates using TVPs.

CREATE TABLE Category
 (Id   int PRIMARY KEY,
  Name nvarchar(max),
  CreatedAt      datetime2(0) DEFAULT SYSDATETIME())

-- Initialize with a few categories
INSERT INTO Category(Id, Name) VALUES(1, 'Housewares')
INSERT INTO Category(Id, Name) VALUES(2, 'Maternity')
INSERT INTO Category(Id, Name) VALUES(3, 'Mens Apparel')
INSERT INTO Category(Id, Name) VALUES(4, 'Womens Apparel')
INSERT INTO Category(Id, Name) VALUES(5, 'Bath')
INSERT INTO Category(Id, Name) VALUES(6, 'Automotive')

-- View the list of categories
SELECT * FROM Category

-- Will be used by uspUpdateCategories to pass in a set of category updates
CREATE TYPE EditedCategoriesUdt AS TABLE
 (Id int PRIMARY KEY,
  Name nvarchar(max))
GO

-- Receive multiple rows for the change set and update the Category table
CREATE PROCEDURE uspUpdateCategories(@EditedCategoriesTVP AS EditedCategoriesUdt
 READONLY)
AS
 BEGIN

  -- Update names in the Category table by joining on the TVP by ID
  UPDATE Category
   SET Category.Name = ec.Name
   FROM Category INNER JOIN @EditedCategoriesTVP AS ec ON Category.Id = ec.Id

 END

-- Load up a few changes into a new TVP instance (to categories 1 and 5)
DECLARE @Edits AS EditedCategoriesUdt
INSERT INTO @Edits VALUES(1, 'Gifts & Housewares')
INSERT INTO @Edits VALUES(5, 'Bath & Kitchen')

-- Call the stored procedure
EXECUTE uspUpdateCategories @Edits

-- View the updated names for categories 1 and 5 in the Category table
SELECT * FROM Category
Other -----------------
- SQL Server 2008 R2 : Database Files and Filegroups (part 2)
- SQL Server 2008 R2 : Database Files and Filegroups (part 1)
- Installing SQL Server 2012 : The Installation Process (part 4) - Post Installation Tasks
- Installing SQL Server 2012 : The Installation Process (part 3) - Installing SQL Server 2012 Through the Command Line, Installing SQL Server 2012 Through PowerShell
- Installing SQL Server 2012 : The Installation Process (part 2) - Installing SQL Server 2012 Through the Installation Center
- Installing SQL Server 2012 : The Installation Process (part 1) - SQL Server 2012 Installation Center
- Installing SQL Server 2012 : Preparing the Server, Selecting the Edition
- SQL Server 2012 : SQL Server Architecture - SQL SERVER’S EXECUTION MODEL AND THE SQLOS
- SQL Server 2012 : SQL Server Architecture - THE LIFE CYCLE OF A QUERY (part 3) - A Simple Update Query
- SQL Server 2012 : SQL Server Architecture - THE LIFE CYCLE OF A QUERY (part 2) - Plan Cache
- SQL Server 2012 : SQL Server Architecture - THE LIFE CYCLE OF A QUERY (part 1)
- Protecting SQL Server Data : CELL-LEVEL ENCRYPTION - Views and Stored Procedures (part 2) - Creating the Stored Procedures
- Protecting SQL Server Data : CELL-LEVEL ENCRYPTION - Views and Stored Procedures (part 1) - Creating the View
- Protecting SQL Server Data : Implementing Cell-Level Encryption
- Protecting SQL Server Data : Preparing for Cell-Level Encryption
- Microsoft SQL Server 2008 R2 : Monitoring Replication (part 2) - New and Improved Peer-to-Peer Replication
- Microsoft SQL Server 2008 R2 : Monitoring Replication (part 1) - Replication Monitoring SQL Statements
- Microsoft SQL Server 2008 R2 : Scripting Replication
- Processing and Storing Data in SQL Server 2005 : Data Migration from One Data Store to Another Data Store
- Processing and Storing Data in SQL Server 2005 : Implementing the Record Failure Code
 
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
programming4us programming4us